package at.fhooe.engine.physics;

import java.util.ArrayList;
import java.util.List;

import at.fhooe.engine.event.events.EntityMovedEvent;
import at.fhooe.engine.pandora.Game;
import at.fhooe.engine.physics.forces.Force;
import at.fhooe.engine.time.PandoraTimer;
import at.fhooe.engine.visuals.Visual;

public abstract class PhysicBody {

	private double[] m_position;
	private double[] m_velocity;
	private double[] m_acceleration;

	private double m_mass;
	private List<Force> m_forces;
	private String m_name;

	private double m_time;
	private PandoraTimer m_timer;
	
	private boolean m_isEntity;

	public PhysicBody(String name, boolean isEntity) {
		m_position = new double[3];
		m_velocity = new double[3];
		m_acceleration = new double[3];
		m_forces = new ArrayList<Force>();

		m_name = name;
		m_mass = 1.0;
		m_time = 0.0;
		m_timer = Game.getInstance().getPandoraTimerByName(PandoraTimer.DEFAULT_TIMER_NAME);
		
		m_isEntity = isEntity;
	}

	void update() {
		m_time += m_timer.getDeltaTime();

		if(m_time >= 0.0) {

			for (int i = 0; i < m_position.length; ++i) {
				m_acceleration[i] = computeForces(i, m_time) / m_mass;

				m_position[i] += m_velocity[i] * m_time;
				m_velocity[i] += m_acceleration[i] * m_time;
			}
			
			if(m_isEntity) {
				Game.getInstance().getEventManager().dispatchEvent(new EntityMovedEvent(m_name, getPositionX(), getPositionY(), getAngularPosition()));
			}

			m_time = 0.0;
		}
	}

	private double computeForces(int i, double dt) {
		double acceleration = 0;
		for(Force force: m_forces) {
			if(i == 0) {
				acceleration += force.getAccelerationX(dt, m_position[i], m_velocity[i], getAngularPosition());
			}
			else if(i == 1) {
				acceleration += force.getAccelerationY(dt, m_position[i], m_velocity[i], getAngularPosition());
			}
			else if(i == 2) {
				acceleration += force.getAngularAcceleration(dt, m_position[i], m_velocity[i], getAngularPosition());
			}

		}
		return  acceleration;
	}

	public String getName() {
		return m_name;
	}

	public void setPosition(double x, double y, double z) {
		m_position[0] = x;
		m_position[1] = y;
	}

	public void setAngularPosition(double alpha) {
		m_position[2] = alpha;
	}

	public double getPositionX() {
		return m_position[0];
	}

	public double getPositionY() {
		return m_position[1];
	}

	public double getAngularPosition() {
		return m_position[2];
	}

	public void setVelocity(double velX, double velY, double velZ) {
		m_velocity[0] = velX;
		m_velocity[1] = velY;
	}

	public void setAngularVelocity(double velAngular) {
		m_velocity[2] = velAngular;
	}

	public void setSpeedInDirection(double speed) {
		setVelocity(-speed * Math.sin(getAngularPosition()), speed * Math.cos(getAngularPosition()), 0.0);
	}

	public double getVelocityX() {
		return m_velocity[0];
	}

	public double getVelocityY() {
		return m_velocity[1];
	}

	public double getAngularVelocity() {
		return m_velocity[2];
	}

	public void setAcceleration(double accX, double accY, double accZ) {
		m_acceleration[0] = accX;
		m_acceleration[1] = accY;
	}

	public void setAngularAcceleration(double accAngular) {
		m_acceleration[2] = accAngular;
	}

	//TODO maybe remove some of these functions because they are handled with forces
	public void setAccelerationInDirection(double acceleration) {
		setAcceleration(-acceleration * Math.sin(getAngularPosition()), acceleration * Math.cos(getAngularPosition()), 0.0);
	}

	public double getAccelerationX() {
		return m_acceleration[0];
	}

	public double getAccelerationY() {
		return m_acceleration[1];
	}

	public double getAngularAcceleration() {
		return m_acceleration[2];
	}

	public void addForce(Force force) {
		//Should be quite efficient because there are only a few forces for one physic body
		if(!m_forces.contains(force)) {
			m_forces.add(force);
		}
	}

	public void removeForce(Force force) {
		m_forces.remove(force);
	}

	public void removeAllForces() {
		m_forces.clear();
	}

	public void setMass(double mass) {
		m_mass = mass;
	}

	public double getMass() {
		return m_mass;
	}

	public void setTimer(String timerName) {
		this.m_timer = Game.getInstance().getPandoraTimerByName(timerName);
	}

	public PandoraTimer getTimer() {
		return m_timer;
	}

	public abstract boolean isColliding(PhysicBody physicBody);
	public abstract boolean isColliding(CircleBody circleBody);
	public abstract boolean isColliding(SquareBody squareBody);
}
